<?php
/**
 *+------------------
 * Lflow
 *+------------------
 * Copyright (c) 2023~2030 gitee.com/liu_guan_qing All rights reserved.本版权不可删除，侵权必究
 *+------------------
 * Author: Mr.April(405784684@qq.com)
 *+------------------
 */
declare (strict_types=1);

namespace lflow\core;

use lflow\ckpt\ProcessCkpt;
use lflow\ckpt\ProcessFlow;
use lflow\exceptions\WorkFlowException;
use lflow\model\OrderModel;
use lflow\model\ProcessModel;
use lflow\model\TaskModel;

/**
 * 流程执行过程中所传递的执行对象，其中包含流程定义、流程模型、流程实例对象、执行参数、返回的任务列表
 *
 * @author Mr.April
 * @since  1.0
 */
class Execution extends BaseServices
{
    /**
     * 容器
     */
    private WorkFlowEngine $engine;

    /**
     * 流程定义对象
     */
    private ProcessModel $process;

    /**
     * 流程实例对象
     */
    private OrderModel $order;

    /**
     * 父流程实例
     */
    private OrderModel $parentOrder;

    /**
     * 父流程实例节点名称
     */
    private string $parentNodeName;

    /**
     * 子流程实例节点名称
     */
    private string $childOrderId;

    /**
     * 执行参数
     */
    private string|object|array|null $args;

    /**
     * 操作人
     */
    private string $operator;

    /**
     * 任务
     */
    private TaskModel $task;

    /**
     * 返回的任务列表
     */
    private array $tasks;

    /**
     * 是否已合并
     * 针对join节点的处理
     */
    private bool $isMerged = false;

    /**
     * 返回任务
     *
     * @return
     */
    public function getEngine()
    {
        return $this->engine;
    }

    /**
     * 设置任务
     */
    public function setEngine($engine)
    {
        $this->engine = $engine;
    }

    /**
     * 构造函数，接收流程定义、流程实例对象、执行参数
     *
     * @param \lflow\core\WorkFlowEngine $engine
     * @param \lflow\model\ProcessModel  $process
     * @param \lflow\model\OrderModel    $order
     * @param string|object              $args
     */
    public function execution(WorkFlowEngine $engine, ProcessModel $process, OrderModel $order, string|object $args): void
    {
        if (empty($process) || empty($order)) {
            throw new WorkFlowException("构造Execution对象失败，请检查process、order是否为空");
        }
        $this->engine  = $engine;
        $this->process = $process;
        $this->order   = $order;
        $this->args    = $args;
    }

    /**
     * 根据当前执行对象execution、子流程定义process、当前节点名称产生子流程的执行对象
     *
     * @param \lflow\core\Execution     $execution
     * @param \lflow\model\ProcessModel $process
     * @param string                    $parentNodeName
     */
    public function subExecution(Execution $execution, ProcessModel $process, string $parentNodeName): void
    {
        if ($execution == null || $process == null || $parentNodeName == null) {
            throw new WorkFlowException("构造Execution对象失败，请检查execution、process、parentNodeName是否为空");
        }
        $this->engine         = $execution->getEngine();
        $this->process        = $process;
        $this->args           = $execution->getArgs();
        $this->parentOrder    = $execution->getOrder();
        $this->parentNodeName = $parentNodeName;
        $this->operator       = $execution->getOperator();
    }

    /**
     * 根据当前执行对象execution、子流程定义process、当前节点名称产生子流程的执行对象
     *
     * @param \lflow\core\Execution     $execution
     * @param \lflow\model\ProcessModel $process
     * @param string                    $parentNodeName
     *
     * @return \lflow\core\Execution|null
     */
    public function createSubExecution(Execution $execution, ProcessModel $process, string $parentNodeName): ?Execution
    {
        $sebExecution = new Execution();
        $sebExecution->subExecution($execution, $process, $parentNodeName);
        return $sebExecution;
    }

    /**
     * 获取流程定义对象
     *
     * @return \lflow\model\ProcessModel
     */
    public function getProcess(): ProcessModel
    {
        return $this->process;
    }

    /**
     * 获取流程模型对象
     *
     * @return mixed
     */
    public function getCkpt(): ProcessCkpt
    {
        return $this->process->getData('process_ckpt');
    }

    /**
     * 获取流程实例对象
     *
     * @return \lflow\model\OrderModel
     */
    public function getOrder(): OrderModel
    {
        return $this->order;
    }

    /**
     * 获取执行参数
     *
     * @return object|string
     */
    public function getArgs(): object|string
    {
        return $this->args;
    }

    /**
     * 写入执行参数
     *
     * @param $args
     */
    public function setArgs($args): void
    {
        $this->args = $args;
    }

    /**
     * 返回任务结果集
     *
     * @return array
     */
    public function getTasks(): array
    {
        return $this->tasks ?? [];
    }

    /**
     * 添加任务集合
     *
     * @param array $tasks
     */
    public function addTasks(array $tasks): void
    {
        $this->tasks = $tasks;
    }

    /**
     * 添加任务
     *
     * @param \lflow\model\TaskModel $task
     */
    public function addTask(TaskModel $task)
    {
        $this->tasks[] = $task;
    }

    /**
     * 返回当前操作人ID
     *
     * @return string
     */
    public function getOperator(): string
    {
        return $this->operator;
    }

    /**
     * 设置当前操作人ID
     *
     * @param string $operator
     */
    public function setOperator(string $operator): void
    {
        $this->operator = $operator;
    }

    /**
     * 返回任务
     *
     * @return \lflow\model\TaskModel|null
     */
    public function getTask(): ?TaskModel
    {
        return $this->task ?? null;
    }

    /**
     * 设置任务
     *
     * @param \lflow\model\TaskModel $task
     */
    public function setTask(TaskModel $task): void
    {
        $this->task = $task;
    }

    /**
     * 判断是否已经成功合并
     *
     * @return bool
     */
    public function isMerged(): bool
    {
        return $this->isMerged;
    }

    /**
     * 设置是否为已合并
     *
     * @param bool $isMerged
     */
    public function setMerged(bool $isMerged): void
    {
        $this->isMerged = $isMerged;
    }

    public function getParentOrder(): ?OrderModel
    {
        return $this->parentOrder ?? null;
    }

    public function getParentNodeName(): string
    {
        return $this->parentNodeName;
    }

    public function getChildOrderId(): string
    {
        return $this->childOrderId;
    }

    public function setChildOrderId(string $childOrderId): void
    {
        $this->childOrderId = $childOrderId;
    }

}
